package cn.demoncat.util.es.dao;

import cn.demoncat.util.es.constant.EsConstant;
import cn.demoncat.util.es.util.AggregationUtil;
import cn.demoncat.util.es.util.CriteriaUtil;
import cn.demoncat.util.es.util.EsUtil;
import cn.demoncat.util.es.util.QueryUtil;
import cn.demoncat.util.lang.CollectionUtil;
import cn.demoncat.util.lang.OptionalUtil;
import cn.demoncat.util.lang.ReflectClassUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.constant.StatusConstant.Common;
import cn.demoncat.util.lang.entity.ListStr;
import cn.demoncat.util.lang.entity.Pager;
import cn.demoncat.util.lang.entity.report.SidCount;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.suggest.SortBy;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;

import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * ElasticSearch客户端基础DAO
 * 
 * @param <T> 文档映射类
 * @param <P> 文档id类
 * 
 * @author 延晓磊
 *
 * @since 2020年9月1日
 * 
 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html">JAVA API</a>
 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html">REST API</a>
 * @see <a href="https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients">Spring Data</a>
 */
public abstract class BaseEsDao<T, P> {

	/**
	 * Spring Data ElasticSearch
	 */
	@Autowired
	protected ElasticsearchRestTemplate elasticsearchTemplate;

	// 索引映射类
	protected Class<T> indexClass = this.getIndexClass();

	private static final Logger LOG = LoggerFactory.getLogger(BaseEsDao.class);


	/**
	 * 获取索引映射类
	 */
	public Class<T> getIndexClass(){
		if (indexClass == null) {
			indexClass = ReflectClassUtil.getGeneric(this.getClass(), 0);
		}
		return indexClass;
	}
	
	/**
	 * 获取索引名称
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public String getIndexName() {
		return EsUtil.getIndexName(getIndexClass());
	}
	
	/**
	 * 获取索引坐标
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public IndexCoordinates getIndexCoordinates() {
		return EsUtil.getIndexCoordinates(getIndexClass());
	}

	/**
	 * 保存：不存在时创建，存在时更新
	 * 
	 * @param o
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-2
	 */
	public T save(T o) {
		return elasticsearchTemplate.save(o);
	}

	/**
	 * 批量保存
	 * 
	 * @param os
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public List<T> save(List<T> os) {
		return CollectionUtil.asList(elasticsearchTemplate.save(os));
	}

	/**
	 * 修改字段
	 *
	 * 注：性能低，建议直接save保存；文档不存在时忽略
	 *
	 * @param id
	 * @param param	{field:newValue}
	 * @return true 更新成功，false 文档不存顾
	 * @author 延晓磊
	 * @since 2022年07月29日
	 */
	public boolean update(P id, Map<String,Object> param){
		try {
			elasticsearchTemplate.update(UpdateQuery.builder(StringUtil.toString(id)).withDocument(Document.from(param)).build(), getIndexCoordinates());
			return true;
		}catch (UncategorizedElasticsearchException e){
			if (e.getMessage() != null && e.getMessage().contains("document_missing")) {
				// 文档不存在，忽略
				LOG.warn("更新ElasticSearch文档失败：文档【id = {}】不存在", id);
				return false;
			}else{
				throw e;
			}
		}
	}

	/**
	 * ID删除
	 * 
	 * @param id
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public void delete(P id) {
		elasticsearchTemplate.delete(StringUtil.toString(id), getIndexClass());
	}
	
	/**
	 * 条件删除
	 * 
	 * @param query
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public void delete(Query query) {
		elasticsearchTemplate.delete(query, getIndexClass(), getIndexCoordinates());
	}
	
	/**
	 * 删除索引
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年9月6日
	 */
	public boolean drop() {
		return elasticsearchTemplate.indexOps(getIndexClass()).delete();
	}
	
	/**
	 * ID查询
	 * 
	 * @param id
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public boolean exists(P id) {
		return elasticsearchTemplate.exists(StringUtil.toString(id), getIndexClass());
	}
	
	/**
	 * ID查询
	 * 
	 * @param id
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-2
	 */
	public T getById(P id) {
		return elasticsearchTemplate.get(StringUtil.toString(id), getIndexClass());
	}

	/**
	 * 条件搜索 - 单个
	 * 
	 * @param query
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public T findOne(Query query) {
		return OptionalUtil.get(elasticsearchTemplate.searchOne(query, getIndexClass()), SearchHit::getContent);
	}

	/**
	 * 条件搜索（默认搜索10条，最多搜索10000条）
	 * 
	 * @param query
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public List<T> findAll(Query query) {
		return EsUtil.getList(search(query));
	}

	/**
	 * 条件搜索
	 *
	 * @param query
	 * @param max	返回条数（默认搜索10条，最多搜索10000条）
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public List<T> findAll(Query query, int max) {
		// 页长
		if (query instanceof NativeSearchQuery) {
			((NativeSearchQuery) query).setMaxResults(max);
		} else if (query instanceof CriteriaQuery) {
			((CriteriaQuery) query).setMaxResults(max);
		}else if (query instanceof StringQuery) {
			((StringQuery) query).setMaxResults(max);
		}
		// 搜索
		return this.findAll(query);
	}

	/**
	 * 条件搜索（默认搜索10条，最多搜索10000条）
	 * 
	 * @param query
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public SearchHits<T> search(Query query) {
		return elasticsearchTemplate.search(query, getIndexClass());
	}
	
	/**
	 * 条件搜索（默认搜索10条，最多搜索全部命中）
	 * 
	 * @param query
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public SearchHits<T> searchAll(Query query) {
		return elasticsearchTemplate.search(QueryUtil.hitAll(query), getIndexClass());
	}
	
	/**
	 * 分页查询（搜索10000条）
	 * 
	 * @param query	查询条件：空表示全部
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 */
	public Pager<T> findPage(Query query, Integer pageNum, Integer pageSize, Sort sort){
		// 查询条件
		if (query == null) {
			query = QueryUtil.all();
		}
		// 分页参数
		Pager<T> pager = new Pager<>(pageNum, pageSize);
		// 搜索
		SearchHits<T> result = search(QueryUtil.addPage(query, QueryUtil.getPage(pager, sort)));
		// 转换结果
		pager.setTemp(result);
		pager.bindTotalCount(result.getTotalHits());
		pager.setList(EsUtil.getList(result));
		return pager;
	}
	
	/**
	 * 分页查询（搜索全部命中）
	 * 
	 * @param query	查询条件：空表示全部
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 */
	public Pager<T> findAllPage(Query query, Integer pageNum, Integer pageSize, Sort sort){
		// 查询条件
		if (query == null) {
			query = QueryUtil.all();
		}
		// 分页参数
		Pager<T> pager = new Pager<>(pageNum, pageSize);
		// 搜索
		SearchHits<T> result = search(QueryUtil.addAllPage(query, QueryUtil.getPage(pager, sort)));
		// 转换结果
		pager.setTemp(result);
		pager.bindTotalCount(result.getTotalHits());
		pager.setList(EsUtil.getList(result));
		return pager;
	}
	
	/**
	 * 滚动查询
	 * 
	 * 1、如果返回数不足size，表示滚动完成，应调用findScroll清除
	 * 
	 * @param query		查询条件（同一批滚动查询保持不变，空表示全部）
	 * @param scrollId	滚动编号（上一次滚动查询的标记）
	 * @param timeout	缓存秒数，默认600（10分钟）
	 * @param refresh	是否为刷新（清除旧滚动，重新滚动）
	 * @param size		返回条数（默认10）
	 * 
	 * @return {list:[数据], scrollId:滚动编号, totalCount:总数}
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 * 
	 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.9/paginate-search-results.html#scroll-search-results">REST API</a>
	 */
	public Pager<T> findScroll(CriteriaQuery query, String scrollId, Long timeout, String refresh, Integer size) {
		if (size == null || size <= 0) {
			return findScroll(query, scrollId, timeout, refresh);
		}else {
			if (query == null) {
				query = QueryUtil.build(CriteriaUtil.all());
			}
			return findScroll(QueryUtil.addSize(query, size), scrollId, timeout, refresh);
		}
	}
	
	/**
	 * 滚动查询
	 * 
	 * 1、如果返回数不足size，表示滚动完成，应调用findScroll清除
	 * 
	 * @param query		查询条件（同一批滚动查询保持不变，空表示全部）
	 * @param scrollId	滚动编号（上一次滚动查询的标记）
	 * @param timeout	缓存秒数，默认600（10分钟）
	 * @param refresh	是否为刷新（清除旧滚动，重新滚动）
	 * @param size		返回条数（默认10）
	 * 
	 * @return {list:[数据], scrollId:滚动编号, totalCount:总数}
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 * 
	 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.9/paginate-search-results.html#scroll-search-results">REST API</a>
	 */
	public Pager<T> findScroll(NativeSearchQuery query, String scrollId, Long timeout, String refresh, Integer size) {
		if (size == null || size <= 0) {
			return findScroll(query, scrollId, timeout, refresh);
		}else {
			if (query == null) {
				query = QueryUtil.all();
			}
			return findScroll(QueryUtil.addSize(query, size), scrollId, timeout, refresh);
		}
	}
	
	/**
	 * 滚动查询
	 * 
	 * 1、默认返回10条，可以通过query设置size
	 * 2、如果返回数不足size，表示滚动完成，应调用findScroll清除
	 * 
	 * @param query		查询条件（同一批滚动查询保持不变，空表示全部）
	 * @param scrollId	滚动编号（上一次滚动查询的标记）
	 * @param timeout	缓存秒数，默认600（10分钟）
	 * @param refresh	是否为刷新（清除旧滚动，重新滚动）
	 * 
	 * @return {list:[数据], scrollId:滚动编号, totalCount:总数}
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 * 
	 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.9/paginate-search-results.html#scroll-search-results">REST API</a>
	 */
	public Pager<T> findScroll(Query query, String scrollId, Long timeout, String refresh) {
		// 查询条件
		if (query == null) {
			query = QueryUtil.all();
		}
		// 缓存时间：滚动查询，是在服务端保存查询记录，以便二次查询，所以比较消耗性能
		if (timeout == null || timeout <= 0) {
			timeout = EsConstant.SCROLL_SECOND;
		}
		timeout *= DateConstant.MILLIS_OF_SECOND;
		SearchScrollHits<T> result;
		if (StringUtils.isBlank(scrollId)) {
			// 首次查询
			result = elasticsearchTemplate.searchScrollStart(timeout, query, getIndexClass(), getIndexCoordinates());
		}else {
			if (Common.isYes(refresh)) {
				// 清除旧滚动
				elasticsearchTemplate.searchScrollClear(CollectionUtil.toStrList(scrollId));
				// 重新查询
				result = elasticsearchTemplate.searchScrollStart(timeout, query, getIndexClass(), getIndexCoordinates());
			}else {
				// 滚动查询
				result = elasticsearchTemplate.searchScrollContinue(scrollId, timeout, getIndexClass(), getIndexCoordinates());
			}
		}
		// 返回分页
		Pager<T> pager = new Pager<>();
		pager.setTemp(result);
		pager.setScrollId(result.getScrollId());
		pager.setTotalCount(result.getTotalHits());
		pager.setList(EsUtil.getList(result));
		return pager;
	}
	
	/**
	 * 滚动查询和处理
	 * 
	 * @param query		查询条件
	 * @param timeout	缓存秒数，默认600（10分钟）
	 * @param size		批次条数（默认10）
	 * @param handler	数据处理：fn(批次数据)
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 * 
	 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.9/paginate-search-results.html#scroll-search-results">REST API</a>
	 */
	public void findScroll(NativeSearchQuery query, Long timeout, Integer size, Consumer<List<T>> handler) {
		// 查询条件
		if (query == null) {
			query = QueryUtil.all();
		}
		// 批次条数
		if (size == null) {
			size = 10;
		}
		QueryUtil.addSize(query, size);
		// 缓存时间：滚动查询，是在服务端保存查询记录，以便二次查询，所以比较消耗性能
		if (timeout == null || timeout <= 0) {
			timeout = EsConstant.SCROLL_SECOND;
		}
		timeout *= DateConstant.MILLIS_OF_SECOND;
		// 首次查询
		SearchScrollHits<T> result = elasticsearchTemplate.searchScrollStart(timeout, query, getIndexClass(), getIndexCoordinates());
		if (result.getSearchHits().size() > 0) {
			// 处理
			handler.accept(EsUtil.getList(result));
		}
		String scrollId = result.getScrollId();
		// 滚动查询
		while (result.getSearchHits().size() == size) {
			result = elasticsearchTemplate.searchScrollContinue(scrollId, timeout, getIndexClass(), getIndexCoordinates());
			if (result.getSearchHits().size() > 0) {
				// 处理
				handler.accept(EsUtil.getList(result));
			}
		}
		// 清除滚动
		elasticsearchTemplate.searchScrollClear(CollectionUtil.toStrList(scrollId));
	}
	
	/**
	 * 清除滚动
	 * 
	 * 1、滚动查询，是在服务端保存查询记录，以便二次查询，比较消耗性能。所以滚动到最后一页（数据为空，或小于size）时，尽量主动清理
	 * 
	 * @param scrollId	滚动编号
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-4
	 * 
	 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.9/paginate-search-results.html#scroll-search-results">REST API</a>
	 */
	public void clearScroll(String scrollId) {
		if (StringUtils.isNotBlank(scrollId)) {
			// 清除滚动
			elasticsearchTemplate.searchScrollClear(CollectionUtil.toStrList(scrollId));
		}
	}
	
	/**
	 * 计数
	 * 
	 * @param query	查询条件：空表示全部
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-2
	 */
	public long count(Query query) {
		// 查询条件
		if (query == null) {
			query = QueryUtil.all();
		}
		// 计数
		return elasticsearchTemplate.count(query, getIndexClass());
	}
	
	/**
	 * 分组统计
	 * 
	 * @param query	查询条件
	 * @param field	分组字段（不能是text分词类型）
	 * 
	 * @return [{value:count}]，最多10000条分组
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-5
	 */
	public List<SidCount> groupCount(NativeSearchQuery query, String field) {
		return groupCount(query, field, 10000);
	}
	
	/**
	 * 分组统计
	 * 
	 * @param query	查询条件
	 * @param field	分组字段（不能是text分词类型）
	 * @param size	返回的分组数
	 * 
	 * @return [{value:count}]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-5
	 */
	public List<SidCount> groupCount(NativeSearchQuery query, String field, int size) {
		// 分组搜索
		SearchHits<T> result = search(QueryUtil.addExcludes(QueryUtil.addAgg(query, AggregationUtil.terms(EsConstant.AGG_NAME, field).size(size))));
		// 获取桶
		Terms agg = result.getAggregations().get(EsConstant.AGG_NAME);
		// 分组统计
		List<SidCount> counts = new ArrayList<>();
		agg.getBuckets().forEach(e -> counts.add(new SidCount(e.getKeyAsString(), e.getDocCount())));
		return counts;
	}
	
	/**
	 * 词条建议
	 * 
	 * 1、根据field字段对应的分词器，将text分词
	 * 2、使用分词后的每个term，在field字段的分词索引中，寻找相似的terms，按相似度评分和词频排序
	 * 
	 * @param text		搜索词
	 * @param fields	搜索字段
	 * 
	 * @return [建议词条]
	 * 
	 * @author 延晓磊
	 * @return 
	 *
	 * @since 2020年9月6日
	 * 
	 * @see <a "https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html">REST API</a>
	 */
	public List<String> termSuggest(String text, String...fields) {
		// 建议器
		SuggestBuilder suggest = new SuggestBuilder().setGlobalText(text);
		for (String field : fields) {
			suggest.addSuggestion("s-" + field, SuggestBuilders.termSuggestion(field).size(EsConstant.SUGGEST_SIZE).minWordLength(EsConstant.SUGGEST_MIN_LEN).sort(SortBy.FREQUENCY));
		}
		// 建议搜索
		return suggest(suggest, fields);
	}
	
	/**
	 * 短语建议
	 * 
	 * 1、根据field字段对应的分词器，将text分词
	 * 2、使用分词后的terms，在field字段中查找短语，按相似度评分（包含的term的数量和顺序）排序
	 * 
	 * @param text		搜索词
	 * @param fields	搜索字段
	 * 
	 * @author 延晓磊
	 * @return 
	 *
	 * @since 2020年9月6日
	 * 
	 * @see <a "https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html">REST API</a>
	 */
	public List<String> phraseSuggest(String text, String...fields) {
		// 建议器
		SuggestBuilder suggest = new SuggestBuilder().setGlobalText(text);
		for (String field : fields) {
			suggest.addSuggestion("s-" + field, SuggestBuilders.phraseSuggestion(field).size(EsConstant.SUGGEST_SIZE));
		}
		// 建议搜索
		return suggest(suggest, fields);
	}
	
	/**
	 * 补全建议
	 * <pre>
	 * 1、根据field字段对应的分词器，将text分词
	 * 2、使用分词后的terms，在field字段中查找补全建议
	 * 
	 * 准确度：Completion > Phrase > Term
	 * 命中率：Term > Phrase > Completion
	 * 
	 * 适用场景：补全建议以特殊的格式存储在内存，搜索速度快，所以常于网站搜索框，用户每输入一个字符，就查询一次建议词条
	 * 
	 * 搜索框实现：
	 * 1、创建专用于搜索框的索引：搜索字段（从各类文档收录的词条）、扩展信息（来源文档的类型、ID）
	 * 		"name" : { "type" : "keyword" , "copy_to": [ "content", "suggest" ] },
	 *      "title" : { "type" : "keyword" , "copy_to": [ "content", "suggest" ] },
	 * 2、设置搜索字段为"FieldType.Search_As_You_Type"/"completion"类型
	 * 		"suggest" : { "type" : "completion", "analyzer": "ik_smart" }
	 * 3、ES将搜索字段分词
	 * 4、用户输入 > 使用Completion搜索建议：前缀匹配分词，返回对应的搜索词条；用户输入3个以上字符时，使用中缀匹配搜索（可以使用skipDuplicates防止结果重复，但会影响性能）
	 * 5、如果通过Completion搜索不到数据，可以改用Phrase搜索，最后使用Term搜索，从而在搜索速度、准确度、命中率方面达到平衡
	 * </pre>
	 * @param text		搜索词
	 * @param fields	搜索字段："FieldType.completion"类型
	 * 
	 * @author 延晓磊
	 * @return 
	 *
	 * @since 2020年9月6日
	 * 
	 * @see <a "https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html">REST API</a>
	 */
	public List<String> completionSuggest(String text, String...fields) {
		// 建议器
		SuggestBuilder suggest = new SuggestBuilder().setGlobalText(text);
		for (String field : fields) {
			suggest.addSuggestion("s-" + field, SuggestBuilders.completionSuggestion(field).size(EsConstant.SUGGEST_SIZE));
		}
		// 建议搜索
		return suggest(suggest, fields);
	}


	/**
	 * Suggest搜索
	 *
	 * @param suggest
	 * @param fields
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年02月07日
	 */
	private List<String> suggest(SuggestBuilder suggest, String...fields){
		// 建议搜索：{"suggest": { "text": "kw","s-f1": {"term": { "field": "message" }},"s-f2": {"phrase": {"field": "message"}}}}
		SearchResponse result = elasticsearchTemplate.suggest(suggest, getIndexCoordinates());
		// 整合词条：{"suggest": {"s-f1": [{"text": "k","options": [{"text": "k","score": 1}]},{"text": "w","options": []}],"s-f2":[]}
		List<Suggest.Suggestion.Entry.Option> list = new ArrayList<>();
		Set<String> set = new HashSet<>();
		Suggest.Suggestion sug;
		List<Suggest.Suggestion.Entry> entries;
		List<Suggest.Suggestion.Entry.Option> options;
		for (String field : fields) {
			// 字段
			sug = result.getSuggest().getSuggestion("s-" + field);
			entries = sug.getEntries();
			for(Suggest.Suggestion.Entry entry: entries) {
				// 字段-分词
				if (!entry.getOptions().isEmpty()) {
					options = entry.getOptions();
					for(Suggest.Suggestion.Entry.Option o: options) {
						// 字段-分词-匹配结果，去重
						if (set.add(o.getText().toString())) {
							list.add(o);
						}
					}
				}
			}
		}
		// 非空校验
		if (list.isEmpty()) {
			return ListStr.EMPTY;
		}
		// 综合排序
		list.sort((a,b) -> Float.compare(a.getScore(), b.getScore()));
		if (list.size() > EsConstant.SUGGEST_SIZE) {
			list = list.subList(0, EsConstant.SUGGEST_SIZE);
		}
		// 返回建议词
		return list.stream().map(e -> e.getText().toString()).collect(Collectors.toList());
	}

}
